/*
 * CS 567 GUI - Semester Project - "DecisionLists"
 * Jason Frank
 * Spring 2011
 *
 * This File:  Container for the main center view of the app.  It replaces the center panel (upon
 *             the user changing views) with the desired new view.  I am implementing this container
 *             as a regular view so that it is notified of the desired view change.  Hopefully I'll
 *             implement fading from the old view to the new view to enhance the visual cue of the
 *             changed view.
 *
 *             The main app class only instantiates this class, rather than all possible views that
 *             can occupy the center area. Therefore, this class instantiates all of the center-area views.
 */

package Decision_Lists;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Component;
import java.awt.Composite;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import java.util.TreeMap;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JPanel;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.border.Border;

/*
 */
public class CenterPanel extends JPanel implements DL_ViewInterface /*, ActionListener */ {

  boolean test_fade = false;
  boolean test_change_views = false;
  final String view_name = "CenterPanel";
  boolean is_center_view = false;
  DL_Model model;
  int panelWidthPref  = 850;
  int panelHeightPref = 600;
  DL_ViewInterface current_center_view;
  DL_ViewInterface butterfly;
  DL_ViewInterface spreadsheet;

  DL_ViewInterface initialView; // must be set after the actual view has been instantiated
  // This map holds a ref to all views. The class can use this map to find a ref to the new desired view
  //  when the user selects a different view from the drop-down menu.
  // Making this map static b/c the combobox in HeaderView needs to know which view
  //  corresponds to the string title of the view, so that it can call model.setCurView().
                // Name,   View
  static TreeMap<String, DL_ViewInterface> views_map = new TreeMap<String, DL_ViewInterface>();
  ////////////////////////////////////////////
  ///// FADE MEMBERS /////////////////////////
  private final int fade_min_opacity = 0;
  private final int fade_max_opacity = 255;
  private float fade_alpha;  // used in paintComp (fade handlers set it as fadout_alpha or fadein_alpha)

  private int   fadeout_interval = 20;  // miliseconds between calls to the action listener.
  private int   fadeout_init_delay = 0;
  private Timer fadeout_timer = new Timer(fadeout_interval, null);  // handler func gets set in constructor
  private float fadeout_alpha = 1.0f;  
  private float fadeout_delta = 0.04f;
   // For different type of alpha (0-255 for fg/bg Color obj))
  private int   fadeout_opacity = fade_max_opacity;  // to start with
  private int   fadeout_opacity_change = 10;

  private int   fadein_interval = fadeout_interval;  // miliseconds between calls to the action listener.
  private int   fadein_init_delay = fadeout_init_delay;
  private Timer fadein_timer = new Timer(fadein_interval, null);
  
  private float fadein_alpha = 0f;  // start opacity at 1.0 (fully opace)
  private float fadein_delta = 0.1f;
  // For different type of alpha (0-255 for fg/bg Color obj))
  private int   fadein_opacity = fade_min_opacity;
  private int   fadein_opacity_change = 10;

  //private JLayeredPane layeredPane;
  //Integer layered_level = 0;  // increment as I go

  JPanel newView;  // used in fade timers
  JPanel oldView;

  Panel_Fader2 pf;
  Panel_Fader3 pf3;

  /////////// END OBJECT VARS ///////////////////
  ///////////////////////////////////////////////
  
  ///////////////////
  // Constructor
  public CenterPanel(DL_Model model) throws Exception  {
    
    this.model = model;           // link the model to this view
    this.model.addObserver(this, this.view_name, is_center_view); // link this view to the model

    // INITIAL LAYOUT
    setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
    setBorder(BorderFactory.createEmptyBorder(0,15,20,15));  // top,left,bottom,right

    // INSTANTIATE ALL CENTER VIEWS and add them to map
    butterfly = new ButterflyView(model);
    views_map.put(butterfly.getViewName(), butterfly);
    spreadsheet = new SpreadsheetView(model);
    views_map.put(spreadsheet.getViewName(), spreadsheet);

    // Set the FADE EVENT HANDLERs
    fade_alpha = fadeout_alpha;  // initialize the generic alpha that paintComponent uses.
    fadeout_timer.addActionListener(fadeout_handler);
          // fadeout_timer.addActionListener(this);
    fadeout_timer.setInitialDelay(fadeout_init_delay);
    fadein_timer.addActionListener(fadein_handler);
    fadein_timer.setInitialDelay(fadein_init_delay);

    // Set the CURRENT VIEW in this center panel
    current_center_view = butterfly;
    add((Component)current_center_view);
    // Must also set the current view in the model
    initialView = butterfly;
    model.setInitialView(initialView);

    // Trying to use the Panel_Fader obj as a strategy obj to do the fading
    pf = new Panel_Fader2(this, this.getGraphics(), oldView, newView);

    //pf3 = new Panel_Fader3(this, (JPanel)current_center_view);

    //////////////// // <editor-fold defaultstate="collapsed" desc="Trying LayerdPanel">
    //// Trying the LayeredPane  ///////
    //    layeredPane = new JLayeredPane();
    //    layeredPane.setPreferredSize(this.getPreferredSize());  // take up the whole panel
    //    //   layeredPane.setBorder(BorderFactory.createTitledBorder("This is a JLayeredPane"));
    //
    //
    //    //((Component)current_center_view).setBounds(0,0,100,100);
    //    layeredPane.setLayout(new FlowLayout());
    //    layeredPane.add((Component) current_center_view, layered_level);
    //    layered_level++;
    //
    //    //layeredPane.moveToBack((Component) current_center_view);
    //    //layeredPane.moveToFront((Component) current_center_view);
    //    add(layeredPane);
    //int z = getComponentZOrder(this);
    //setComponentZOrder(layeredPane, z +1);

    // repaint(); // trying to get the rectangle drawn on top// </editor-fold>
    
  } ///// end constructor /////

  // FADE OUT handler
  ActionListener fadeout_handler = new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {

      fadeout_alpha -= fadeout_delta;
      // Stop the timer if the panel is fully faded out.
      if(fadeout_alpha < 0) {
        fadeout_alpha = 0;  // the AlphaComposite requires alpha to be [0.0,1.0]
        fade_alpha =  fadeout_alpha;  // set the generic alpha that paintComponent uses.
        fadeout_timer.stop();
        remove((Component)current_center_view);
        current_center_view = (DL_ViewInterface) newView;
        add((Component)newView);
        fadein_timer.start();
      }///
      repaint();

      fade_alpha =  fadeout_alpha;  // set the generic alpha that paintComponent uses.
      if(test_fade) System.out.println("fadeout_handler just set fade_alpha to: " + fade_alpha);
  
      // <editor-fold defaultstate="collapsed" desc="Fading FG/BG way">
      /*
      fadeout_opacity -= fadeout_opacity_change;

      if (fadeout_opacity < fade_min_opacity) {
        fadeout_opacity = fade_min_opacity;
        fadeout_timer.stop();
        remove((Component) current_center_view);
        current_center_view = (DL_ViewInterface) newView;
        fadein_timer.start();
      }
      Color old_bg = oldView.getBackground();
      Color old_fg = oldView.getForeground();
      oldView.setBackground(new Color(old_bg.getRed(), old_bg.getGreen(), old_bg.getBlue(), fadeout_opacity));
      oldView.setForeground(new Color(old_fg.getRed(), old_fg.getGreen(), old_fg.getBlue(), fadeout_opacity));
      //oldView.setBackground(new Color(0, 0, 0, fade_max_opacity - fadeout_opacity));
      if (test_fade) {
        System.err.println("actionPerformed just set fadeout_opacity to: " + fadeout_opacity);

      }
      repaint(); */// </editor-fold>    
    }
  }; ///// end FADE OUT handler ////

  // FADE IN handler
  ActionListener fadein_handler = new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
      // <editor-fold defaultstate="collapsed" desc="Fading FG/BG way">
      /*
      // Add the new JPanel to this panel at the beginning of the is timer sequence
      if(fadein_opacity == fade_min_opacity) {
      add((Component)newView);
      }
      fadein_opacity += fadein_opacity_change;

      if(fadein_opacity > fade_max_opacity ) {
      fadein_opacity = fade_max_opacity;
      fadein_timer.stop();
      //finish_change_views();
      //TODO: finish this part?
      }
      Color new_bg = newView.getBackground();
      Color new_fg = newView.getForeground();
      newView.setBackground(new Color(new_bg.getRed(), new_bg.getGreen(), new_bg.getBlue(), fadein_opacity));
      newView.setForeground(new Color(new_fg.getRed(), new_fg.getGreen(), new_fg.getBlue(), fadein_opacity));

      if(test_fade) System.err.println("actionPerformed just set fadein_opacity to: " + fadein_opacity);
      repaint();
       */// </editor-fold>

      fadein_alpha += fadein_delta;
      // Stop the timer if the panel is fully faded out.
      if(fadein_alpha > 1.0 ) {
        fadein_alpha = 1.0f;  // the AlphaComposite requires alpha to be [0.0,1.0]
        fade_alpha =  fadein_alpha;  // set the generic alpha that paintComponent uses.
        fadein_timer.stop();
      }///
      repaint();

      fade_alpha =  fadein_alpha;  // set the generic alpha that paintComponent uses.
      if(test_fade) System.out.println("fadein_handler just set fade_alpha to: " + fade_alpha);
      
    } /////////// end actionPerformed() (for the fadein_handler) //////
  };

  // Not using now
  public void repackFrame() {
    //////////////////////////////////////////
    //// GET THE ENTIRE VIEW RE-LAYED OUT ////
    invalidate();
    // Call pack() on the parent JFrame so that this panel will be re-layed out.
    JFrame frame = (JFrame) SwingUtilities.getRoot(this);
    frame.pack();

    repaint();  // zero-arg verion of repaint() repaints entire panel
  }



  @Override
  public void paintComponent(Graphics g) {
    super.paintComponent(g);

    pf.paint_caller_panel(g);
/*
            // Save orig graphics before modifying it
            // Graphics myG = g.create();
            //    Graphics2D g2 = (Graphics2D) myG; // 2D needed for composite information
    // MUST USE THE ORIGINAL G to make the fade work!
    Graphics2D g2 = (Graphics2D) g; // 2D needed for composite information
    /////////////////////////////////////////////////////////////////////////
    ///// SETUP "COMPOSITE" TO ENABLE ALPHA FADING (by timer & handler) /////

    // tutorial at http://java.sun.com/products/jfc/tsc/articles/swing2d/
            // Save orig composite - DO NOT RESTORE THE OLD COMPOSITE (to make the fade work!)
            // Composite oldComposite = g2.getComposite();
    // Create an AlphaComposite
    Composite fadeout_composite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, fade_alpha);
    // Set the composite on the Graphics2D object.
    g2.setComposite(fadeout_composite);

    if(test_fade) System.err.println("paintComponent just did setComposit() with fade_alpha: " + fade_alpha);

    g2.setPaint(getBackground()); // draw on top with same color as background
    g2.fillRect(0, 0, getWidth(), getHeight());

            // Restore the orig composite? - DO NOT RESTORE THE OLD COMPOSITE (to make the fade work!)
            //    g2.setComposite(oldComposite);  // DO NOT RESTORE THE OLD COMPOSITE (to make the fade work!)

            // myG.dispose();
 */
  } //// end paintComponent() ///////

        //        public void call_super_paintComponent() {
        //         super.paintComponent(getGraphics());
        //        }



  /////////////////////////////////////////////////
  ////// Methods Required by the Interface ////////

  // The Model calls this to notify this View of a changed current view
  // This method removes the previous center view and adds the new center view.
  public void curViewChanged(String old_view_name, String new_view_name ) {

    /////////////////////////
    //// START FADES   //////

    // Resets
    fadein_opacity = fade_min_opacity;
    fadeout_opacity = fade_max_opacity;
    fadein_alpha = 0.0f;
    fadeout_alpha = 1.0f;

    newView = (JPanel) views_map.get(new_view_name);
    oldView = (JPanel) current_center_view;

//    fadeout_timer.start();  // calls fadeout_handler() at specified intervals

      // Can't get this to work (using the "Strategy Pattern" [using another obj to do the fade])
      // Panel_Fader2 pf = new Panel_Fader2(this, this.getGraphics(), oldView, newView);
    pf.setFadein_panel(newView);
    pf.setFadeout_panel(oldView);
    pf.startFade();
    // <editor-fold defaultstate="collapsed" desc="Repacking (not using now- it resized whole window [usually smaller than what I had it at])">
   /*
    //////////////////////////////////////////
    //// GET THE ENTIRE VIEW RE-LAYED OUT ////
    invalidate();
    // Call pack() on the parent JFrame so that this panel will be re-layed out.
    JFrame frame = (JFrame) SwingUtilities.getRoot(this);
    frame.pack();

    if(test_change_views)
    System.err.println("CenterPanel just attempted to change the center view from <" + old_view_name
    + "> to <" + new_view_name + ">");

    repaint();  // zero-arg verion of repaint() repaints entire panel
     */// </editor-fold>
  } ///// end curViewChanged() //////////

  // Model calls this to inform views of an added (new) list item.
  //  The views will then probably need to re-layout their list items to reflect the change.
  public void itemAdded(List_Item_Data newItem) {
    // Do nothing
  }
  // Model calls this to inform views of a changed list item (i.e., new item added, old item deleted, etc).
  //  The views will then probably need to re-layout their list items to reflect the change.
  public void itemChanged() {
    // Do nothing, this view doesn't care about new list items
  }

  public String getViewName() {
    return this.view_name;
  } //// end getViewName() /////////

  /////// end required by interface functions  //////
  ///////////////////////////////////////////////////


  ////// SETTERS ////
  public void setCurrent_center_view(DL_ViewInterface view) {
    this.current_center_view = view;
  }



  @Override
  public Dimension getMinimumSize()   { return getPreferredSize(); }
  @Override  // TODO: set the width as the width of the parent
  public Dimension getPreferredSize() { return new Dimension(panelWidthPref, panelHeightPref); }

} //////////////////// end class CenterPanel //////////////////

